package br.edu.ufcg.splab.parser;

/**
 * The ParserShortAnnotated create a shorter Graph which each transitions carries the own type 
 * (transitions don't need another transition before itself to specify the type).
 * @author Gustavo Yamaguchi  
 */

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import br.edu.ufcg.splab.core.InterfaceEdge;
import br.edu.ufcg.splab.core.InterfaceGraph;
import br.edu.ufcg.splab.core.InterfaceVertex;
import br.edu.ufcg.splab.core.edges.EdgeType;
import br.edu.ufcg.splab.core.graph.Graph;
import br.edu.ufcg.splab.exceptions.LTSBTException;

public class ParserShortAnnotated {

	private InterfaceGraph graph;
	private Graph newGraph;
	private Map<String, InterfaceVertex> aux;

	/**
	 * Recover the new Graph 
	 * @return The shorter Graph
	 * @throws LTSBTException
	 */
	public InterfaceGraph graphContraction(InterfaceGraph graph) throws LTSBTException{
		aux = new HashMap<String, InterfaceVertex>();
		this.graph = graph;
		newGraph = new Graph();		
		makeLinks();
		return newGraph;
	}
	
	private void makeLinks() throws LTSBTException {
		List<InterfaceEdge> edges = graph.getEdges();
		for (InterfaceEdge edge : edges) {
			rearrenge(edge.getFrom(), edge);
		}
	}

	private void rearrenge(InterfaceVertex vertexFrom, InterfaceEdge edge) throws LTSBTException {		
		if(areEqualsAllInTransitions(vertexFrom)){
			if(!isDefault(edge)){	
				for(InterfaceEdge next_edge: edge.getTo().getOutTransitions()){				
					if(!isDefault(next_edge) & edge.getLabel().equals(next_edge.getLabel())){
						rearrenge(vertexFrom, next_edge);
					}else if(isDefault(next_edge) & !containsInTransitions(vertexFrom,edge)){
						createEdge(vertexFrom, edge, next_edge);						
					}else if(!isDefault(next_edge) & !edge.getLabel().toUpperCase().equals(next_edge.getLabel().toUpperCase())){
						throw new LTSBTException("Invalid graph: two edges followed of different types ("+ edge.getFrom().getLabel() + ", " + edge.getLabel() + ", "+ edge.getTo().getLabel() + ", " + next_edge.getLabel() +  ", " + next_edge.getTo().getLabel() +").");
					}
				}
			}else{
				for(InterfaceEdge next_edge: edge.getTo().getOutTransitions()){
					if(isDefault(next_edge)){
						throw new LTSBTException("Invalid graph: two edges followed of the default type ("+ EdgeType.DEFAULT.getStrType() + ", "+ edge.getTo().getLabel() + ", " + EdgeType.DEFAULT.getStrType() +  ", " + next_edge.getTo().getLabel() +").");
					}
				}
			}
		}
	}
	
	private void createEdge(InterfaceVertex vertexFrom, InterfaceEdge edge, InterfaceEdge next_edge) throws LTSBTException {
		EdgeType type = EdgeType.getInstance(edge.getLabel().trim());				
		InterfaceVertex vf = aux.get(vertexFrom.getLabel());
		InterfaceVertex vt = aux.get(next_edge.getTo().getLabel());
		
		if(vf == null && vt == null){
			UUID uuid_from = newGraph.addVertex(vertexFrom.getLabel());
			UUID uuid_to = newGraph.addVertex(next_edge.getTo().getLabel());
			newGraph.createEdge(
					uuid_from,
					uuid_to,
					next_edge.getLabel(), 
					type);
			aux.put(vertexFrom.getLabel(), newGraph.getStates().get(uuid_from));
			aux.put(next_edge.getTo().getLabel(), newGraph.getStates().get(uuid_to));
		}else if(vf != null && vt == null){
			UUID uuid_to = newGraph.addVertex(next_edge.getTo().getLabel());
			newGraph.createEdge(
					vf.getUUID(),
					uuid_to,
					next_edge.getLabel(), 
					type);
			aux.put(next_edge.getTo().getLabel(), newGraph.getStates().get(uuid_to));
		}else if(vf == null && vt != null){
			UUID uuid_from = newGraph.addVertex(vertexFrom.getLabel());
			newGraph.createEdge(
					uuid_from,
					vt.getUUID(),
					next_edge.getLabel(), 
					type);
			aux.put(vertexFrom.getLabel(), newGraph.getStates().get(uuid_from));
		}else{
			newGraph.createEdge(
					vf.getUUID(),
					vt.getUUID(),
					next_edge.getLabel(), 
					type);
		}
	}
	
	private boolean areEqualsAllInTransitions(InterfaceVertex vertex) throws LTSBTException {	
		String type = null;
		String strType = "";
		for(InterfaceEdge edge: vertex.getInTransitions()){
			if(type == null){
				if(isDefault(edge)){
					type = EdgeType.DEFAULT.getStrType();
				}				
				strType += type;
			}else if(isDefault(edge)){
				if(!type.equals(EdgeType.DEFAULT.getStrType())){
					strType += " and " + EdgeType.DEFAULT.getStrType();
					throw new LTSBTException("Invalid graph: the inputs are different edge types for the vertex " + edge.getTo().getLabel() + " (" + strType +").");
				}
			}else if(!type.toLowerCase().equals(edge.getLabel().toLowerCase())){
				strType += " and " + edge.getLabel();
				throw new LTSBTException("Invalid graph: the inputs are different edge types for the vertex " + edge.getTo().getLabel() + " (" + strType +").");
			}
		}
		return true;
	}
	
	private boolean containsInTransitions(InterfaceVertex vertex, InterfaceEdge edge){
		for(InterfaceEdge e: vertex.getInTransitions()){
			if(e.getLabel().toUpperCase().equals(edge.getLabel().toUpperCase())){
				return true;
			}
		}
		return false;
	}
		
	private boolean isDefault(InterfaceEdge edge) {
		if (edge.getLabel().toLowerCase().equals("steps")
				|| edge.getLabel().toLowerCase().equals("conditions")
				|| edge.getLabel().toLowerCase().equals("expected_results")) {
			return false;
		}
		return true;
	}

}
